#!/usr/bin/python
#
#--------------------------------------------------------------------
# Data Structures
#--------------------------------------------------------------------
# label: { 
#     url: url,
#     stuffs: 
#     {
#        item : { pattern:pattern, message:message, expression:[lambda, p1, p2], matched:[ val, ref ] },
#        ...
#     },
#     topic: @topic
# }
#
#---------------------------------------------------------------------
# Class Structures
#---------------------------------------------------------------------
# ** box   
#       {
#           @url:url,
#           @stuffs: {
#               @@item:stuff,
#               ...
#           },
#       }
#
# ** sutff
#       { 
#           @pattern:pattern
#           @message:message
#           @expression: [ lambda, p1, p2 ]
#           @matched: [ val, ref ]}
#       }
#
import logging
from Predef import Predef as PRE
from google.appengine.ext import ndb
from parser import parser as ROCK


class Stuff(ndb.Model):
    date = ndb.DateTimeProperty(required=True, auto_now_add=True)

    pattern = ndb.StringProperty(required=True, indexed=False)
    message = ndb.StringProperty(required=True, indexed=False)

    expression = ndb.StringProperty(required=True, indexed=False)
    val = ndb.StringProperty(default='0', indexed=False)
    ref = ndb.StringProperty(default='0', indexed=False)
    p1 = ndb.StringProperty(default='0', indexed=False)
    p2 = ndb.StringProperty(default='0', indexed=False)

    ############
    # process
    ############
    def ID(self):
        return self.date.strftime('%Y%m%d%H%M%S%f')
    
    def isMatched(self):
        return eval("lambda val,ref,p1,p2:%s" % self.expression)(self.val, self.ref, self.p1, self.p2)

    def keepref(self):
        self.ref = self.val

class Box(ndb.Model):
    ############
    # Initialize
    ############
    label = ndb.StringProperty(required=True)
    url = ndb.StringProperty(default=PRE.url_)
    description = ndb.StringProperty(indexed=False,default='')
    stuffs = ndb.StructuredProperty(Stuff, repeated=True, indexed=False)
    isRunning = ndb.BooleanProperty(default=False)

    ############
    # process
    ############
    def lauchIt(self):
        self.isRunning=True
        self.put()

    def drawbackIt(self):
        self.isRunning=False
        self.put()

    def flush(self):
        for k in self.stuffs:
            if k.isMatched():
                k.keepref()

    def hasUpdate(self):
        for k in self.stuffs:
            if k.isMatched():
                return True
        return False

    def report(self,isReportAll=False, isSkipMatch=False, isHTML=False):
        Content=[]
        if isSkipMatch or self.hasUpdate():
            for k in self.stuffs:
                if isSkipMatch or isReportAll or k.isMatched():
                    c = k.message.format(k.val,k.ref)
                    Content.append("<p>{}</p>".format(c)
                             if isHTML else c)

        if len(Content)>0:
            # add desciption and header
            if isHTML:
                return '<p>{}</p>-----------------------<p>{}</p>-----------------------<p>Source: {}</p>-----------------------'.format(
                    self.label,'\n'.join(Content), self.url)
            else:
                return '{}\n-----------------------\n{}\n-----------------------\nSource: {}\n-----------------------'.format(
                    self.label,'\n'.join(Content), self.url)

        return ''

    ############
    # class
    ############
    @classmethod
    def getMenu(cls, Running=False):
        res=[]
        ll=cls.listBoxes(Running=True)
        for x in ll:
            res.append("{}. {} - {}".format(ll.index(x), x.label, x.description))

        return '\n'.join(res)

    @classmethod
    def listBoxes(cls, Running=False):
        query = cls.query(cls.isRunning == Running)
        return query.fetch()

    @classmethod
    def getBox(cls, label):
        query = cls.query(cls.label == label)
        return query.get()

    @classmethod
    def getBoxByNo(cls, n):
        query = cls.query(cls.isRunning == True).fetch( limit=1 , offset=n )
        if len(query)>0:
            return query[0]
        else:
            return None

    @classmethod
    def delBox(cls, label):
        query = cls.query(cls.label == label)
        return query.get().key.delete()
        
    @classmethod
    def rockMe(cls):
        for x in cls.listBoxes(Running=True):
            x.flush()
            ROCK(x).run()
            x.put()

'''
####################################################
# stuff
####################################################
class stuff:
    ############
    # Initialize
    ############
    def __init__(self, pattern, message, expression):
        self.pattern = pattern
        self.message = message
        self.expression = PRE.checkExpression(expression)
        self.matched = list(PRE.matched_)

    def update(self, s):
        self.pattern = s.pattern
        self.message = s.message
        self.expression = PRE.checkExpression(s.expression)
        #self.matched = PRE.checkMatched(s.matched)
 
    ############
    # verify
    ############
    def isValid(self):
        return (self.pattern!='') and (self.message!='') and (self.expression[0]!='') 

    ############
    # scatter & gather
    ############
    def set(self,value):
        self.matched[0]=value

    def getPattern(self):
        return self.pattern

    def getMessage(self):
        return self.message

    def getExpression(self):
        return PRE.checkExpression(self.expression)

    def getMatched(self):
        return PRE.checkMatched(self.matched)

    def setMatched(self, m):
        self.matched=PRE.checkMatched(m)

    ############
    # process
    ############
    def export(self):
        # pattern
        p="'{}'".format("':r'".join([PRE.pattern , self.pattern]))
        # message
        m="'{}'".format("':'".join([PRE.message , self.message]))
        #expression
        e="'"+PRE.expression+"':"+"[r'{}']".format("','".join(PRE.checkExpression(self.expression)))
        # matched
        c="'"+PRE.matched+"':"+"['{}']".format("','".join(self.matched))
        # stuff
        return "{"+",".join([p,m,e,c])+"}"

    def keepref(self):
        self.matched[1]=self.matched[0]


####################################################
# box
####################################################
class box:

    ############
    # Initialize
    ############
    def __init__(self,label):
        self.label=label
        self.url=PRE.url_
        self.contact=list(PRE.contact_)
        self.stuffs=dict()

    def update(self, pack):
        self.url=pack[PRE.url]
        self.contact=pack[PRE.contact]
        for k,v in pack[PRE.stuffs].items():
            self.addItem(k, v[PRE.pattern], v[PRE.message], v[PRE.expression], v[PRE.matched])
        return self

    ############
    # verify
    ############
    def isValid(self):
        for s in self.stuffs.values():
            if not s.isValid():
                return False
        return True

    ############
    # scatter & gather
    ############
    def getUrl(self):
        return self.url

    def setUrl(self,url):
        if PRE.isValidHttp(url):
            self.url=url
            return True
        else:
            return False

    def getLabel(self):
        return self.label


    ############
    # set up Items
    ############
    def delItem(self, item):
        if item in self.stuffs:
            del self.stuffs[item]
            return True
        else:
            logging.error("Failed to delItem, because <%s> has been found in lists.", item)
            #raise NameError, item
        return False

    def addItem(self, item, pattern, message, expression, matched=None):
        if (item != '') and (item not in self.stuffs):
            s=stuff(pattern, message, PRE.checkExpression(expression))
            if matched is not None:
                s.setMatched(matched)
            if s.isValid():
                self.stuffs[item]=s
                return True
            else:
                logging.error("Failed to addItem, because it was invalid.")
        else:
            logging.error("Failed to addItem, because <%s> has existed.", item)
            #raise NameError, item
        return False

    def updateItem(self, item, pattern, message, expression):
        if item in self.stuffs:
            s=stuff(pattern, message, PRE.checkExpression(expression))
            if s.isValid():
                self.stuffs[item].update(s)
                return True
            else:
                logging.error("Failed to updateItem, because it was invalid.")
        else:
            logging.error("Failed to updateItem, because <%s> has been found in lists.", item)
            #raise NameError, item
        return False

    def setItemValue(self, item, value):
        if item in self.stuffs:
            self.stuffs[item].set(value)
            return True
        else:
            logging.error("Failed to setItemValue, because <%s> has been found in lists.", item)
            #raise NameError, item
        return False

    ############
    # process
    ############
    def keepref(self):
        for stuff in self.stuffs.values():
            stuff.keepref()

    def export(self):
        # url
        u="'{}'".format("':'".join([PRE.url , self.url]))
        # stuffs
        #",".join("'{0}':{1}".format(k,v.out()) for k,v in items.items()
        s="'" + PRE.stuffs + "':{" + ",".join("'{0}':{1}".format(k,v.export()) for k,v in self.stuffs.items())+"}"
        # contact
        c="'" + PRE.contact + "':"+"['{}']".format("','".join(self.contact))
        # box
        #return "'"+self.label+"':{"+",".join([u,s,c])+"}"
        return "{{{}}}".format(",".join([u,s,c]))
'''
